/*
 * Copyright (c) 2012, marco.tamburelli@gmail.com
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met: 
 * 
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer. 
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution. 
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.bmi.gwt.mi.shared;

import org.bmi.gwt.mi.shared.exceptions.InvalidArgumentException;
import org.bmi.gwt.mi.shared.exceptions.NotAvailableConstructorException;
import org.bmi.gwt.mi.shared.util.ProxyBase;
import org.bmi.gwt.mi.shared.util.ProxyFactory;
import org.bmi.gwt.mi.shared.util.UtilityFunctions;

/**
 * Interface which enables to access functionalities available for a queue pool
 * provided by an expression.
 * 
 * A queue pool is a set of queues matching an expression of type
 * 
 * <pre>
 *     org.test.something.*
 * </pre>
 * 
 * it means that all queues whose name prefix is <code>org.test.something</code>
 * belong to the pool. Note that a queue pool can be empty in such case any
 * publish event will have no effect.
 * 
 * @author marco.tamburelli@gmail.com
 */
public abstract class BroadChannel
{
	protected String queueExpression;
	protected ProxyFactory proxyFactory;

	/**
	 * Returns the expression for the pool.
	 * 
	 * @return The expression.
	 */
	public String getExpression()
	{
		return this.queueExpression;
	}

	/**
	 * This method returns a generated proxy which pushes methods to clients.
	 * Calling this method provokes the creation of an element on each listening
	 * client. <br>
	 * 
	 * Note that this way to launch the creation of an object could be slow.
	 * Creating locally an object and registering it has better performance.
	 * 
	 * @param proxyType An interface class extending {@link MiProxy} and
	 *        defining methods to be pushed.
	 * @param constructorArgs Arguments to be pushed to the constructor.
	 * @return A proxy object implementing the provided interface extending
	 *         {@link MiProxy}.
	 * @throws InvalidArgumentException When the target object doesn't allow a
	 *         constructor compatible with provided parameters.
	 */
	@SuppressWarnings("unchecked")
	public <T extends MiProxy> T proxyToNewObject(Class<T> proxyType, Object... constructorArgs)
			throws InvalidArgumentException
	{
		String objectId = UtilityFunctions.generateUniqueId();

		MiPublisher publisher = provideMiPublisher(false);
		publisher.setBroadChannel(this);

		try
		{
			ProxyBase proxy = this.proxyFactory.createProxy(proxyType, constructorArgs);
			ProxyBase.setObjectId(proxy, objectId);
			ProxyBase.setPublisher(proxy, publisher);
			ProxyBase.publishConstructor(proxy, constructorArgs);

			return (T) proxy;
		}
		catch (NotAvailableConstructorException e)
		{
			throw new InvalidArgumentException("Provided arguments don't match any provided constructor", e);
		}
	}

	/**
	 * This method returns a proxy which pushes methods to a known object bound
	 * to one. To avoid unpredictable behaviors the provided object id should be
	 * a valid id from an already created object.
	 * 
	 * @param proxyType An interface class extending {@link MiProxy} and
	 *        defining methods to be pushed.
	 * @param objectId The unique id of an object.
	 * @return A proxy object implementing the provided interface extending
	 *         {@link MiProxy}.
	 */
	@SuppressWarnings("unchecked")
	public <T extends MiProxy> T proxyToObject(Class<T> proxyType, String objectId)
	{
		MiPublisher publisher = provideMiPublisher(false);
		publisher.setBroadChannel(this);

		ProxyBase proxy = this.proxyFactory.createProxy(proxyType);
		ProxyBase.setObjectId(proxy, objectId);
		ProxyBase.setPublisher(proxy, publisher);

		return (T) proxy;
	}

	/**
	 * This method creates a transaction within this queue.
	 * 
	 * @return The requested transaction.
	 */
	public abstract MiTransaction createTransaction();

	protected abstract MiPublisher provideMiPublisher(boolean transactional);
}
